package wsz.rpc.consumer.proxy;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import wsz.rpc.common.RpcRequest;
import wsz.rpc.common.RpcResponse;
import wsz.rpc.consumer.client.RpcClient;
import wsz.rpc.zk.ZkClient;

import javax.annotation.PostConstruct;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 客户端连接代理类
 * @author wsz
 * @date 2021/12/01 18:29
 **/
@Component
public class RpcClientProxy {

    @Value("${zkServerUrl}")
    private String zkServerUrl;
    // 缓存代理类
    private Map<String, Object> proxyMap = new HashMap<>();

    private ZkClient zkClient;

    @PostConstruct
    private void initZkData() {
        zkClient = new ZkClient(zkServerUrl);
    }

    /**
     * 代理类中进行rpc调用
     * @param serviceClass
     * @return
     */
    public Object createProxy(Class serviceClass) {
        String className = serviceClass.getName();
        if (!proxyMap.containsKey(className)) {
            Object proxyTarget =  Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class[]{serviceClass},
                    (proxy, method, args) -> {
                        RpcRequest rpcRequest = new RpcRequest();
                        rpcRequest.setRequestId(UUID.randomUUID().toString());
                        rpcRequest.setClassName(method.getDeclaringClass().getName());
                        rpcRequest.setMethodName(method.getName());
                        rpcRequest.setParameterTypes(method.getParameterTypes());
                        rpcRequest.setParameters(args);

                        // TODO 负载均衡
                        RpcClient rpcClient = zkClient.randomClient();
                        long start = System.currentTimeMillis();
                        try {
                            Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRequest));

                            RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);
                            if (rpcResponse.getError() != null) {
                                throw new RuntimeException(rpcResponse.getError());
                            }
                            Object result = rpcResponse.getResult();
                            if (result == null) {
                                return null;
                            }
                            return JSON.parseObject(result.toString(), method.getReturnType());
                        } catch (Exception ex) {
                            throw ex;
                        } finally {
                            long end = System.currentTimeMillis();
                            zkClient.handleTime(rpcClient, start, end);
                        }
                    });
            proxyMap.put(className, proxyTarget);
        }
        return proxyMap.get(className);
    }
}
